/**
 * 
 */
package ro.dta.idbi.ui.sql;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.event.EventListenerList;

import ro.dta.idbi.interfaces.IMemento;
import ro.dta.idbi.interfaces.IProject;
import ro.dta.idbi.interfaces.IntegrationOperation;
import ro.dta.idbi.interfaces.ui.IExecIqlDialog;
import ro.dta.idbi.interfaces.ui.IExecTransDialog;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.model.SaveStrategyEps;
import ro.dta.idbi.model.SaveStrategyPng;
import uk.ac.ic.doc.automed.IntegrityException;
import uk.ac.ic.doc.automed.NotFoundException;
import uk.ac.ic.doc.automed.reps.Schema;

/**
 * Panel used to display the schema as a diagram or in textual form
 * 
 * @author Tudor Dobrila
 * 
 */
public class SqlSchemaPanel extends JPanel implements ActionListener {

	private static final long serialVersionUID = 1559991046319942835L;

	/**
	 * Parent frame of the application
	 */
	protected transient IMainFrame parentFrame;

	/**
	 * Schema currently displayed in the panel
	 */
	protected transient Schema schema;

	/**
	 * Diagram displayed in the panel
	 */
	private SqlSchemaDiagram diagram;

	/**
	 * Project containing this schema panel
	 */
	private final IProject project;

	/**
	 * Undo button, used to reverse the schema to a previous state
	 */
	private JButton undoButton;

	/**
	 * Event listener list
	 */
	private final transient EventListenerList listenerList = new EventListenerList();

	/**
	 * Sql schema panel class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of the application
	 * @param schema
	 *            Schema to be displayed in the panel
	 * @param project
	 *            Integration project that is used in conjunction with this SQL
	 *            diagram
	 * @throws Exception
	 */
	public SqlSchemaPanel(final IMainFrame parentFrame, final Schema schema, final IProject project) {
		super();
		this.parentFrame = parentFrame;
		this.schema = schema;
		this.project = project;
		setLayout(new BorderLayout());

		this.schema = schema;
		IntegrationOperation operation = null;
		if (project != null) {
			operation = project.getCurrentOperation();
		}

		diagram = new SqlSchemaDiagram(parentFrame, schema, operation);
		add(diagram, BorderLayout.CENTER);

		createBottomMenu();

		diagram.addSchemaChangedEventListener(new SchemaChangedListener() {

			@Override
			public void schemaChanged(SchemaChangedEvent event) {
				undoButton.setEnabled(true);
			}
		});
	}

	/**
	 * Sql schema panel class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of the application
	 * @param schema
	 *            Schema to be displayed in the panel
	 * @throws Exception
	 */
	public SqlSchemaPanel(final IMainFrame parentFrame, final Schema schema) {
		this(parentFrame, schema, null);
	}

	/**
	 * Create bottom menu with Arrange, Zoom In / Out options
	 */
	private void createBottomMenu() {
		final JToolBar bottomPanel = new JToolBar();
		bottomPanel.setFloatable(false);
		bottomPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
		add(bottomPanel, BorderLayout.NORTH);

		if (project != null) {
			undoButton = new JButton(new ImageIcon("./resources/undo.png"));
			undoButton.addActionListener(this);
			undoButton.setName("undotrans");
			undoButton.setToolTipText("Undo previous transformation");
			undoButton.setEnabled(false);
			bottomPanel.add(undoButton);
		}

		final JButton arrangeButton = new JButton(new ImageIcon("./resources/arrange.png"));
		arrangeButton.addActionListener(this);
		arrangeButton.setName("arrange");
		arrangeButton.setToolTipText("Auto-arrange the diagram");
		bottomPanel.add(arrangeButton);

		final JButton zoomInButton = new JButton(new ImageIcon("./resources/zoom_in.png"));
		zoomInButton.addActionListener(this);
		zoomInButton.setName("zoomin");
		zoomInButton.setToolTipText("Zoom In");
		bottomPanel.add(zoomInButton);

		final JButton zoomOutButton = new JButton(new ImageIcon("./resources/zoom_out.png"));
		zoomOutButton.addActionListener(this);
		zoomOutButton.setName("zoomout");
		zoomOutButton.setToolTipText("Zoom out");
		bottomPanel.add(zoomOutButton);

		final JButton execQueryButton = new JButton(new ImageIcon("./resources/query.png"));
		execQueryButton.addActionListener(this);
		execQueryButton.setName("execsql");
		execQueryButton.setToolTipText("Execute IQL Query");
		bottomPanel.add(execQueryButton);

		if (project != null) {
			final JButton execButton = new JButton(new ImageIcon("./resources/exec.png"));
			execButton.addActionListener(this);
			execButton.setName("exec");
			execButton.setToolTipText("Execute Commands");
			bottomPanel.add(execButton);

			final JButton execTransButton = new JButton(new ImageIcon("./resources/exec_trans.png"));
			execTransButton.addActionListener(this);
			execTransButton.setName("exectrans");
			execTransButton.setToolTipText("Execute BAV Transaction");
			bottomPanel.add(execTransButton);
		}

		final JButton exportButton = new JButton(new ImageIcon("./resources/png.png"));
		exportButton.addActionListener(this);
		exportButton.setName("export");
		exportButton.setToolTipText("Export as PNG");
		bottomPanel.add(exportButton);

		final JButton epsButton = new JButton(new ImageIcon("./resources/eps.png"));
		epsButton.addActionListener(this);
		epsButton.setName("eps");
		epsButton.setToolTipText("Export as EPS");
		bottomPanel.add(epsButton);
	}

	@Override
	public void actionPerformed(final ActionEvent event) {
		final JComponent source = (JComponent) event.getSource();

		if (source.getName().equals("arrange")) {
			diagram.performLayout();
		}

		if (source.getName().equals("zoomin")) {
			diagram.getGraphComponent().zoomIn();
		}

		if (source.getName().equals("zoomout")) {
			diagram.getGraphComponent().zoomOut();
		}

		if (source.getName().equals("execsql")) {
			final IExecIqlDialog dialog = parentFrame.getGuiFactory().createExecIqlDialog(
					parentFrame, schema, "");
			dialog.setVisible(true);
		}

		if (source.getName().equals("exec") || source.getName().equals("exectrans")) {
			boolean isTransaction = source.getName().equals("exectrans");
			final IExecTransDialog dialog = parentFrame.getGuiFactory().createExecTransDialog(
					parentFrame, schema, isTransaction);
			dialog.setVisible(true);
			if (dialog.getTransformationResult() != null) {
				diagram.setSchema(schema, dialog.getTransformationResult());
			}
		}

		if (source.getName().equals("export")) {
			diagram.save(new SaveStrategyPng());
		}

		if (source.getName().equals("eps")) {
			diagram.save(new SaveStrategyEps());
		}

		if (source.getName().equals("undotrans")) {
			try {
				IMemento oldSchema = project.undoSchema(schema.getName());
				setDiagram(Schema.getSchema(oldSchema.getOriginalSchema()),
						project.getCurrentOperation());
				fireSchemaUndoEvent(new SchemaUndoEvent(this, oldSchema));
				parentFrame.getRepositoryBrowser().refresh();
				parentFrame.getHistoryPanel().removeCommand(oldSchema.getCommand());
				if (!project.canUndo(oldSchema.getOriginalSchema())) {
					undoButton.setEnabled(false);
				}
			} catch (NotFoundException e) {
				JOptionPane
						.showMessageDialog(
								this,
								"Could not revert to a previous version. Original schema does not exist any more!",
								"Error!", JOptionPane.ERROR_MESSAGE);
			} catch (IntegrityException e) {
				JOptionPane.showMessageDialog(this,
						"Could not revert to a previous version. Reason:\r\n" + e.getMessage(),
						"Error!", JOptionPane.ERROR_MESSAGE);
			}
		}

	}

	/**
	 * @param schema
	 *            Schme to display in the panel
	 * @param operation
	 *            Current integration operation
	 */
	public void setDiagram(final Schema schema, final IntegrationOperation operation) {
		this.schema = schema;

		try {
			if (diagram == null) {
				diagram = new SqlSchemaDiagram(parentFrame, schema, operation);
				add(diagram, BorderLayout.CENTER);
				revalidate();
				repaint();
			} else {
				diagram.setSchema(schema, operation);
			}

		} catch (Exception e) {
			JOptionPane.showMessageDialog(this,
					"Could not display diagram.\r\nReason: " + e.getMessage(), "Error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	/**
	 * @return The diagram displayed in the panel
	 */
	public SqlSchemaDiagram getDiagram() {
		return diagram;
	}

	public void addSchemaUndoEventListener(final SchemaUndoListener listener) {
		listenerList.add(SchemaUndoListener.class, listener);
	}

	public void removeSchemaUndoListener(final SchemaUndoListener listener) {
		listenerList.remove(SchemaUndoListener.class, listener);
	}

	private void fireSchemaUndoEvent(final SchemaUndoEvent evt) {
		final Object[] listeners = listenerList.getListenerList();

		for (int i = 0; i < listeners.length; i += 2) {
			if (listeners[i] == SchemaUndoListener.class) {
				((SchemaUndoListener) listeners[i + 1]).schemaUndo(evt);
			}
		}
	}

}
